
#include <EEPROM.h>

#include "app.hpp"
#include "storage.hpp"


static bool storage_buffer_changed = false;
static bool storage_buffer_filled = false;


constexpr size_t STORAGE_START_POS = 0;


#ifndef STM32_CORE_VERSION

void eeprom_buffer_fill() {}


uint8_t eeprom_buffered_read_byte(int p) {
    return EEPROM.read(p);
}


void eeprom_buffered_write_byte(int p, uint8_t b) {
    EEPROM.write(p, b);
}

void eeprom_buffer_flush() {}

#endif


/**
 * @brief 从Flash 加载菜单项数据
 * 
 * @param count    需要加载前count 个菜单项
 * @return size_t 
 */
size_t load_from_storage(MenuItem *items, uint8_t count) {
    eeprom_buffer_fill();
    storage_buffer_filled = true;
    size_t pos = STORAGE_START_POS;

    u8 b = eeprom_buffered_read_byte(pos);
    if(b != 0) {
        // 第一字节对应第一个菜单项的index 值，如果不为0，
        // 表示flash 存储尚未被初始化，停止加载，使用菜单默认值
        return 0;
    }

    for (int i = 0; i < count; ++i) {
        auto &item = items[i];
        // 读取index
        item.index = eeprom_buffered_read_byte(pos);
        ++pos;
        // 读取data
        char *data_ptr = reinterpret_cast<char *>(&item.data);
        for(unsigned j = 0; j < sizeof(MenuItemData); ++j) {
            data_ptr[j] = eeprom_buffered_read_byte(pos + j);
        }

        pos += sizeof(MenuItemData);
    }

    return count;
}


/**
 * @brief 将寄存器数据写入Flash
 * 
 * 数据不会立即被写入Flash，而是先写入RAM 里的缓冲区，稍后再由定时任务task_update_storage 实际写入flash。
 * 这么做的目的是把数据写入放在空闲时间里执行，避免影响程序实时性。
 * 
 * @param arr 
 * @return size_t 
 */
size_t save_to_storage(MenuItem *items, uint8_t count) {
    size_t pos = STORAGE_START_POS;

    for (int i = 0; i < count; ++i) {
        auto &item = items[i];
        // 写入index
        eeprom_buffered_write_byte(pos, item.index);
        ++pos;
        // 读取data
        char *data_ptr = reinterpret_cast<char *>(&item.data);
        for(unsigned j = 0; j < sizeof(MenuItemData); ++j) {
            eeprom_buffered_write_byte(pos + j, data_ptr[j]);
        }

        pos += sizeof(MenuItemData);
    }

    storage_buffer_changed = true;
    return count;
}


/**
 * @brief 每500 毫秒检查一次，如果存储数据发生了修改，就把数据写到flash
 * 
 * 更新数据前必须先调用load_from_storage，将flash 数据同步到RAM。
 * 
 * @return TimeType 
 */
TimeType task_update_storage() {
    if (storage_buffer_filled) {
        if (storage_buffer_changed) {
            eeprom_buffer_flush();
            storage_buffer_changed = false;
        }
    }

    return 500;
}